/*
 ------------------------------------------------------------------------------
 Copyright (C) 2009-2011 Eternal Games.

 This file is part of the Eternal Tech source code.

 The Eternal Tech source code is free software; you can redistribute it and/or
 modify it under the terms of the GNU General Public License as published by
 the Free Software Foundation; either version 2 of the License, or (at your
 option) any later version.

 The Eternal Tech source code is distributed in the hope that it will be useful,
 but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
 or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License for
 more details.

 You should have received a copy of the GNU General Public License along with
 the Eternal Tech source code; if not, write to the Free Software Foundation, Inc.,
 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301, USA.
 ------------------------------------------------------------------------------
*/


//
// cl_tempents.cpp - Client side temporary entities
//


#include "cl_local.h"
#include "client.h"


/*
 ==============================================================================

    ENTITY BEAM PARSING

 ==============================================================================
*/

#define MAX_BEAMS		32

enum beamType_t {
	BT_PARASITE,
	BT_GRAPPLE
};

struct beam_t {
	beamType_t	type;
	int			entity;
	int			destEntity;
	int			endTime;
	vec3_t		start;
	vec3_t		end;
	vec3_t		offset;
};

static beam_t	cl_beams[MAX_BEAMS];


/*
 ==================
 CL_ParseParasite
 ==================
*/
static int CL_ParseParasite (sizebuf_t *msg)
{
	beam_t  *b;
	int		entity;
	vec3_t	start, end;
	int		i;

	entity = MSG_ReadShort(msg);
	MSG_ReadPos(msg, start);
	MSG_ReadPos(msg, end);

	// Override any beam with the same entity
	for (i = 0, b = cl_beams; i < MAX_BEAMS; i++, b++)
	{
		if (b->entity == entity)
		{
			b->type = BT_PARASITE;
			b->entity = entity;
			b->endTime = cl.time + 200;
			VectorCopy(start, b->start);
			VectorCopy(end, b->end);
			VectorClear(b->offset);

			return entity;
		}
	}

	// Find a free beam
	for (i = 0, b = cl_beams; i < MAX_BEAMS; i++, b++)
	{
		if (b->endTime < cl.time)
			break;
	}

	if (i == MAX_BEAMS)
	{
		Com_DevPrintf(S_COLOR_RED "CL_ParseParasite: list overflow\n");
		return entity;
	}

	b->type = BT_PARASITE;
	b->entity = entity;
	b->endTime = cl.time + 200;
	VectorCopy(start, b->start);
	VectorCopy(end, b->end);
	VectorClear(b->offset);

	return entity;
}

/*
 ==================
 CL_ParseGrapple
 ==================
*/
static int CL_ParseGrapple (sizebuf_t *msg)
{
	beam_t  *b;
	int		entity;
	vec3_t	start, end, offset;
	int		i;

	entity = MSG_ReadShort(msg);
	MSG_ReadPos(msg, start);
	MSG_ReadPos(msg, end);
	MSG_ReadPos(msg, offset);

	// Override any beam with the same entity
	for (i = 0, b = cl_beams; i < MAX_BEAMS; i++, b++)
	{
		if (b->entity == entity)
		{
			b->type = BT_GRAPPLE;
			b->entity = entity;
			b->endTime = cl.time + 200;
			VectorCopy(start, b->start);
			VectorCopy(end, b->end);
			VectorCopy(offset, b->offset);
			
			return entity;
		}
	}

	// Find a free beam
	for (i = 0, b = cl_beams; i < MAX_BEAMS; i++, b++)
	{
		if (b->endTime < cl.time)
			break;
	}

	if (i == MAX_BEAMS)
	{
		Com_DevPrintf(S_COLOR_RED "CL_ParseGrapple: list overflow\n");
		return entity;
	}

	b->type = BT_GRAPPLE;
	b->entity = entity;
	b->endTime = cl.time + 200;
	VectorCopy(start, b->start);
	VectorCopy(end, b->end);
	VectorCopy(offset, b->offset);

	return entity;
}

/*
 ==================
 CL_AddBeams

 TODO: Fix cl_hand
 ==================
*/
void CL_AddBeams ()
{
	beam_t			 *b;
	int				 i;
	renderEntity_t   ent;
	vec3_t			 org, vec, angles;
	float			 dist, length, handMult;

	if (cl_hand->integerValue == 2)
		handMult = 0;
	else if (cl_hand->integerValue == 1)
		handMult = -1;
	else
		handMult = 1;

	for (i = 0, b = cl_beams; i < MAX_BEAMS; i++, b++)
	{
		if (b->endTime < cl.time)
			continue;

		if (b->type != 3)
		{
			// If coming from the player, update the start position
			if (b->entity == cl.playernum+1)
			{
				b->start[0] = cl.renderView.viewOrigin[0];
				b->start[1] = cl.renderView.viewOrigin[1];
				b->start[2] = cl.renderView.viewOrigin[2] - 22;
			}

			VectorAdd(b->start, b->offset, org);
			VectorSubtract(b->end, org, vec);
		}
		else
		{
			// If coming from the player, update the start position
			if (b->entity == cl.playernum+1)
			{
				// Set up gun position
				VectorLerp(cl.oldPlayerState->gunoffset, cl.playerState->gunoffset, cl.lerpFrac, b->start);
				VectorAdd(b->start, cl.renderView.viewOrigin, b->start);

				VectorMA(b->start, -(handMult * b->offset[0]), cl.renderView.viewAxis[1], org);
				VectorMA(org, b->offset[1], cl.renderView.viewAxis[0], org);
				VectorMA(org, b->offset[2], cl.renderView.viewAxis[2], org);

				if (cl_hand->integerValue == 2)
					VectorMA(org, -1, cl.renderView.viewAxis[2], org);

				VectorSubtract(b->end, org, vec);

				length = VectorLength(vec);
				VectorScale(cl.renderView.viewAxis[0], length, vec);

				VectorMA(vec, -(handMult * b->offset[0]), cl.renderView.viewAxis[1], vec);
				VectorMA(vec, b->offset[1], cl.renderView.viewAxis[0], vec);
				VectorMA(vec, b->offset[2], cl.renderView.viewAxis[2], vec);
			}
			else
			{
				VectorAdd(b->start, b->offset, org);
				VectorSubtract(b->end, org, vec);
			}
		}

		// Add the beam entity
		memset(&ent, 0, sizeof(renderEntity_t));

		if (b->type != BT_PARASITE)
		{
			ent.reType = RE_BEAM;
			VectorCopy(org, ent.origin);
			VectorAdd(org, vec, ent.beamEnd);
			MakeRGBA(ent.materialParms, 1.0f, 1.0f, 1.0f, 1.0f);

			switch (b->type){
			case BT_GRAPPLE:
				ent.beamWidth = 2;
				ent.beamLength = 30;
				ent.customMaterial = cl.media.grappleBeamMaterial;
				break;
			}

			R_AddEntityToScene(&ent);
			continue;
		}

		VectorToAngles(vec, angles);
		angles[2] = rand() % 360;

		ent.reType = RE_MODEL;
		ent.model = cl.media.modParasiteBeam;
		ent.scale = 1;
		AnglesToAxis(angles, ent.axis);
		MakeRGBA(ent.materialParms, 1.0f, 1.0f, 1.0f, 1.0f);

		dist = VectorNormalize(vec);
		length = (dist - 30) / (ceil(dist / 30) - 1);

		while (dist > 0){
			VectorCopy(org, ent.origin);

			R_AddEntityToScene(&ent);

			VectorMA(org, length, vec, org);
			dist -= 30;
		}
	}
}

/*
 ==================
 CL_ClearBeams
 ==================
*/
void CL_ClearBeams ()
{
	memset(cl_beams, 0, sizeof(cl_beams));
}


/*
 ==============================================================================

    TEMPORARY ENTITY PARSING

 ==============================================================================
*/


/*
 ==================
 CL_ParseTempEntity

 TODO: Add particle effects and finish some of the TE_* flags
 ==================
*/
void CL_ParseTempEntity (sizebuf_t *msg)
{
	int		type;
	vec3_t	pos, pos2, dir;
	int		count, color;
	int		i;

	type = MSG_ReadByte(msg);

	// Handle the entity type
	switch (type){
	case TE_BLOOD:
		MSG_ReadPos(msg, pos);
		MSG_ReadDir(msg, dir);
		
		// TODO: Add particle effect

		break;
	case TE_GUNSHOT:
		MSG_ReadPos(msg, pos);
		MSG_ReadDir(msg, dir);

		//CL_TracerTrail(pos, dir, cl.media.tracerParticle);
		CL_BulletParticles(pos, dir, cl.media.bulletParticle, cl.media.bulletParticle2);

		i = rand() & 15;
		if (i >= 1 && i <= 3)
			S_StartSound(pos, 0, 0, cl.media.sfxRichotecs[i-1], 1, ATTN_NORM, 0);

		break;
	case TE_SHOTGUN:
		MSG_ReadPos(msg, pos);
		MSG_ReadDir(msg, dir);

		//CL_TracerTrail(pos, dir, cl.media.tracerParticle);
		CL_BulletParticles(pos, dir, cl.media.bulletParticle, cl.media.bulletParticle2);

		break;
	case TE_SPARKS:
	case TE_BULLET_SPARKS:
		MSG_ReadPos(msg, pos);
		MSG_ReadDir(msg, dir);

		// TODO: Add particle effect

		S_StartSound(pos, 0, 0, cl.media.sfxLaserHit, 1, ATTN_NORM, 0);

		break;
	case TE_SCREEN_SPARKS:
	case TE_SHIELD_SPARKS:
		MSG_ReadPos(msg, pos);
		MSG_ReadDir(msg, dir);

		// TODO: Add particle effect

		S_StartSound(pos, 0, 0, cl.media.sfxLaserHit, 1, ATTN_NORM, 0);

		break;
	case TE_LASER_SPARKS:
		count = MSG_ReadByte(msg);
		MSG_ReadPos(msg, pos);
		MSG_ReadDir(msg, dir);
		color = MSG_ReadByte(msg);

		// TODO: Add particle effect

		break;
	case TE_SPLASH:
		count = MSG_ReadByte(msg);
		MSG_ReadPos(msg, pos);
		MSG_ReadDir(msg, dir);
		color = MSG_ReadByte(msg);

		// TODO: Add particle effect

		break;
	case TE_BLUEHYPERBLASTER:
		MSG_ReadPos(msg, pos);
		MSG_ReadDir(msg, dir);

		break;
	case TE_BLASTER:
		MSG_ReadPos(msg, pos);
		MSG_ReadDir(msg, dir);

		CL_BlasterParticles(pos, dir, cl.media.blasterParticle,cl.media.blasterParticle2);
		S_StartSound(pos, 0, 0, cl.media.sfxLaserHit, 1, ATTN_NORM, 0);

		break;
	case TE_RAILTRAIL:
		MSG_ReadPos(msg, pos);
		MSG_ReadPos(msg, pos2);

		CL_RailTrail(pos, pos2, cl.media.railTrailParticle, cl.media.railTrailParticle2);
		S_StartSound(pos2, 0, 0, cl.media.sfxRailgun, 1, ATTN_NORM, 0);

		break;
	case TE_GRENADE_EXPLOSION:
	case TE_GRENADE_EXPLOSION_WATER:
		MSG_ReadPos(msg, pos);
		VectorSet(dir, 0.0f, 0.0f, 1.0f);

		if (type == TE_GRENADE_EXPLOSION_WATER)
		{
			// TODO: Add particle effect

			CL_BubbleParticles(pos, 384, 30, cl.media.bubbleParticle);
			S_StartSound(pos, 0, 0, cl.media.sfxWaterExplosion, 1, ATTN_NORM, 0);
		}
		else
		{
			// TODO: Add particle effect

			S_StartSound(pos, 0, 0, cl.media.sfxGrenadeExplosion, 1, ATTN_NORM, 0);
		}

		break;
	case TE_EXPLOSION1:
	case TE_EXPLOSION2:
	case TE_ROCKET_EXPLOSION:
	case TE_ROCKET_EXPLOSION_WATER:
		MSG_ReadPos(msg, pos);

		if (type == TE_ROCKET_EXPLOSION_WATER)
		{
			// TODO: Add particle effect

			CL_BubbleParticles(pos, 384, 30, cl.media.bubbleParticle);
			S_StartSound(pos, 0, 0, cl.media.sfxWaterExplosion, 1, ATTN_NORM, 0);
		}
		else
		{
			// TODO: Add particle effect

			S_StartSound(pos, 0, 0, cl.media.sfxRocketExplosion, 1, ATTN_NORM, 0);
		}

		break;
	case TE_EXPLOSION1_NP:
	case TE_EXPLOSION1_BIG:
	case TE_PLAIN_EXPLOSION:
	case TE_PLASMA_EXPLOSION:
		MSG_ReadPos(msg, pos);

		break;
	case TE_BFG_EXPLOSION:
	case TE_BFG_BIGEXPLOSION:
		MSG_ReadPos(msg, pos);

		if (type == TE_BFG_BIGEXPLOSION)
		{
			// TODO: Add particle effect
		}
		else
		{
			// TODO: Add particle effect
		}

		break;
	case TE_BFG_LASER:
		MSG_ReadPos(msg, pos);
		MSG_ReadPos(msg, pos2);

		// TODO: Add a beam entity

		break;
	case TE_BUBBLETRAIL:
		MSG_ReadPos(msg, pos);
		MSG_ReadPos(msg, pos2);

		CL_BubbleTrail(pos, pos2, 32, 1, cl.media.bubbleTrailParticle);

		break;
	case TE_PARASITE_ATTACK:
	case TE_MEDIC_CABLE_ATTACK:
		CL_ParseParasite(msg);

		break;
	case TE_GRAPPLE_CABLE:
		CL_ParseGrapple(msg);

		break;
	case TE_BOSSTPORT:
		MSG_ReadPos(msg, pos);

		// TODO: Add particle effect?

		S_StartSound(pos, 0, 0, S_RegisterSound("misc/bigtele.wav"), 1, ATTN_NONE, 0);

		break;
	case TE_WELDING_SPARKS:
		count = MSG_ReadByte(msg);
		MSG_ReadPos(msg, pos);
		MSG_ReadDir(msg, dir);
		color = MSG_ReadByte(msg);

		break;
	case TE_TUNNEL_SPARKS:
		count = MSG_ReadByte(msg);
		MSG_ReadPos(msg, pos);
		MSG_ReadDir(msg, dir);
		color = MSG_ReadByte(msg);

		break;
	case TE_GREENBLOOD:
		MSG_ReadPos(msg, pos);
		MSG_ReadDir(msg, dir);

		break;
	case TE_MOREBLOOD:
		MSG_ReadPos(msg, pos);
		MSG_ReadDir(msg, dir);

		break;
	case TE_BLASTER2:
		MSG_ReadPos(msg, pos);
		MSG_ReadDir(msg, dir);

		break;
	case TE_FLECHETTE:
		MSG_ReadPos(msg, pos);
		MSG_ReadDir(msg, dir);

		break;
	case TE_LIGHTNING:
		MSG_ReadPos(msg, pos);
		MSG_ReadDir(msg, dir);

		break;
	case TE_FLASHLIGHT:
		MSG_ReadPos(msg, pos);
		MSG_ReadShort(msg);

		break;
	case TE_FORCEWALL:
		MSG_ReadPos(msg, pos);
		MSG_ReadPos(msg, pos2);

		// TODO: Add a force wall entity?

		break;
	case TE_HEATBEAM:
		break;
	case TE_MONSTER_HEATBEAM:
		break;
	case TE_HEATBEAM_SPARKS:
		MSG_ReadPos(msg, pos);
		MSG_ReadDir(msg, dir);

		break;
	case TE_HEATBEAM_STEAM:
		MSG_ReadPos(msg, pos);
		MSG_ReadDir(msg, dir);

		break;
	case TE_STEAM:
		break;
	case TE_BUBBLETRAIL2:
		MSG_ReadPos(msg, pos);
		MSG_ReadPos(msg, pos2);

		break;
	case TE_CHAINFIST_SMOKE:
		MSG_ReadPos(msg, pos);

		break;
	case TE_ELECTRIC_SPARKS:
		MSG_ReadPos(msg, pos);
		MSG_ReadDir(msg, dir);

		break;
	case TE_TRACKER_EXPLOSION:
		MSG_ReadPos(msg, pos);

		break;
	case TE_TELEPORT_EFFECT:
	case TE_DBALL_GOAL:
		MSG_ReadPos(msg, pos);

		break;
	case TE_WIDOWBEAMOUT:
		MSG_ReadShort(msg);
		MSG_ReadPos(msg, pos);

		break;
	case TE_WIDOWSPLASH:
		MSG_ReadPos(msg, pos);

		break;
	case TE_NUKEBLAST:
		MSG_ReadPos(msg, pos);

		break;
	default:
		Com_Error(ERR_DROP, "CL_ParseTempEntity: bad entity type (%i)", type);
	}
}